package cn.shen.edit.ftp;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import sun.net.ftp.FtpClient;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@RequestMapping("/test")
@RestController
@Slf4j
public class FtpController {

    @PostMapping("/ftpupload")
    public String uploadFileToFtp(@RequestBody MultipartFile file, HttpServletResponse response) throws Exception {
//        if (file==nullfile.getSize()){
//            log.info(文件为空);
//            return WebResultUtil.genErrorResult(文件为空);
//        }

        String LOCAL_CHARSET = "UTF-8";
        FTPClient ftpClient = this.initFtpClient();
        InputStream inputStream = null;
        String filePath = "optftp_data";
        try {
            inputStream = file.getInputStream();
            if (FTPReply.isPositiveCompletion(ftpClient.sendCommand(
                    "OPTS UTF8", "ON"))) { //开启服务器对UTF-8的支持，如果服务器支持就用UTF-8编码，否则就使用本地编码（GBK）.
                LOCAL_CHARSET = "UTF-8";
            }
            ftpClient.setControlEncoding(LOCAL_CHARSET);
            response.setHeader("Access-Control-Allow-Origin","*" );
            String b = file.getName();
            String a = file.getOriginalFilename();
            ftpClient.enterLocalPassiveMode();
            ftpClient.changeWorkingDirectory(filePath);
            ftpClient.makeDirectory(file.getOriginalFilename());
            ftpClient.changeWorkingDirectory(filePath);

            ftpClient.storeFile(new String(file.getOriginalFilename().getBytes("UTF-8"),
                    "ISO-8859-1"),inputStream);

            ftpClient.storeFile(file.getOriginalFilename(),inputStream);
            inputStream.close();
            ftpClient.logout();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (ftpClient.isConnected()) {
                try {
                    ftpClient.disconnect();
                } catch (IOException e) {
                    log.info("销毁失败upload{}", e);
                    e.printStackTrace();
                }
            }
            if (null != inputStream) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    log.info("关闭流失败upload{}", e);
                    e.printStackTrace();
                }
            }
        }


//        return WebResultUtil.genSuccessResult();
        return "";
    }

    @CrossOrigin
    @GetMapping(value = "/ftp/download",produces = {MediaType.APPLICATION_OCTET_STREAM_VALUE, MediaType.APPLICATION_JSON_VALUE})
    public void downloadFileStream(HttpServletResponse response, HttpServletRequest request, String path, String fileName, String buildRange
            , @RequestHeader String range) throws IOException{
        int a = 0;
        log.info("第{}次range请求,请求头为{}",++a,request.getHeader("Range"));
        OutputStream outputStream = null;
        path = "/opt/ftp_data/";
//        fileName = "cinatest.txt";
//        path = "optwacossmpnasexclusive";
//        fileName = "test8.ts";
         FTPClient ftpClient = this.initFtpClient();
        String LOCAL_CHARSET = "UTF-8";
        String allPath =  path + fileName ;
        InputStream in = null;
        BufferedInputStream bis = null;
        try {
//            int index = path.indexOf();
//            int index1 = path.indexOf(, index + 2);
            //文件路径 去除掉ftp地址
//            String filePath = path.substring(index1);
            String filePath = path;
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            String ext = fileName.substring(fileName.lastIndexOf(".")).toLowerCase();

//            设置文件ContentType类型，这样设置，会自动判断下载文件类型
//            response.setContentType("applicationx-msdownload");
//            response.setContentType(videox-mpg);
//            response.setContentType("applicationoctet-stream");
//            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
//            response.setHeader("Access-Control-Expose-Headers", "start");
//            response.setHeader("Access-Control-Expose-Headers", "end");
//            response.setHeader(Connection, keep-alive);
            response.setHeader("Access-Control-Allow-Origin", "*");
            String aa= URLEncoder.encode(fileName, "UTF-8");
//            设置文件头：最后一个参数是设置下载的文件名并编码为UTF-8
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8")+ext);
//            此句代码适用Linux的FTP服务器
            String newPath = new String(fileName.getBytes("GBK"), StandardCharsets.ISO_8859_1);
            ftpClient.enterLocalPassiveMode();   //设置被动模式，开通一个端口来传输数据

            List<String> rangeList = Arrays.stream(range.replaceAll("bytes="," ").split(",")).collect(Collectors.toList());
//            获取ftp文件数据
            FTPFile[] ftpFiles = ftpClient.listFiles(new String(allPath.getBytes(LOCAL_CHARSET), StandardCharsets.ISO_8859_1));

            for (FTPFile file:ftpFiles){
                boolean result = existFile(ftpClient,filePath);
                if (result) {

//                    当前第几分片
                    long segmentNum = 1;
//                    设置分片大小
                    long segmentSize = (long) (5 * 1024 * 1024);
//                    获取总分片数量   向下取整,分片从1开始
//                    long segmentNumMax = (long) Math.ceil(file.getSize()  segmentSize);
//                    设置上传的起始偏移量, 断点续传用到
//                    ftpClient.setRestartOffset(segmentNum  segmentSize);
//                    response.setContentType("multipartbyte/ranges;"boundary=,);
                    ftpClient.changeWorkingDirectory(path);
                    in = ftpClient.retrieveFileStream(newPath);
//                    System.out.println(in----+in);
                    bis = new BufferedInputStream(in);
                    outputStream = response.getOutputStream();
                    byte[] buf = new byte[1024];
                    int len = 0;
                    int i = 0;
                    String rangeObj = request.getHeader("Range");

//                    循环range全部返回
//                    for (String rangeObj:rangeList){
//                    String asw = rangeObj.substring(rangeObj.indexOf("=") + 1, rangeObj.indexOf("-"));
//
                    long start = Long.parseLong(rangeObj.substring(rangeObj.indexOf("=") + 1, rangeObj.indexOf("-")));

                    long end = Math.min(start + segmentSize - 1, file.getSize()-1);
                    log.info("start {},end{}",start,end);
                    long offset = segmentNum * segmentSize;
                    long lenght = end - start;
                    response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
                    bis.skip(start);
//                    设置此次相应返回的数据长度   需要计算下载进度时设置，并且在不确定时不要设置，防止长度过大,浏览器等待剩余数据
                    response.setContentLength((int) segmentSize);
//                            设置此次相应返回的数据范围
//                    long start2 = start + segmentSize;
//                    start2 = start2 > file.getSize() ? file.getSize() : start2;

                    long end2 = end > file.getSize()?file.getSize():end;
                    response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + file.getSize());
                    response.setHeader("Accept-Ranges", "bytes");
                    response.setHeader("start", String.valueOf(start));
                    response.addHeader("end", String.valueOf(end));
                    while ((len = bis.read(buf)) > 0) {
                        outputStream.write(buf, 0, len);
                        outputStream.flush();
                        log.info("正在写入-------------{} ,{}",i++,buf);
                    }

//                    }
//                    循环range全部返回

//                            分段返回
//                    Map<String, Integer> rangeObj = this.analyzeRange(request.getHeader("Range"), (int) file.getSize());
//                    String aswd = "bytes " + rangeObj.get("startByte") + "-" + rangeObj.get("endByte") + "/" + file.getSize();
//                     System.out.println(request.getHeader("Range"));
//                    response.setHeader("Content-Length", String.valueOf(file.getSize()));
//                    long end = Math.min(rangeObj.get("startByte") + segmentSize, file.getSize());
//                    response.setHeader("Content-Range", "bytes " + rangeObj.get("startByte") + "-" + rangeObj.get("startByte")+segmentSize + "/" + file.getSize());
//                    response.setHeader("Accept-Ranges", "bytes");
//                    response.setContentType("video/mp4");
////                    response.setContentType("application/octet-stream");
//                    response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
//                    int length = rangeObj.get("endByte") - rangeObj.get("startByte");
////
//                    byte[] buffer = new byte[length < 1024 ? length : 1024];
//                    int i = in.read(buffer);
//                    int bb =  0;
//                    length = length - buffer.length;
//                    log.info("当前range:--",++a,request.getHeader("Range"));
//                    bis.skip(rangeObj.get("startByte"));
//                    while (i !=-1) {
//                        outputStream.write(buffer, 0, i);
//                         if (length <= 0) {
//                            break;
//                        }
//                        i = in.read(buffer);
//                        length = length - buffer.length;
//
//                        log.info("正在写入-------------{}",bb++);
//                    }

//                    outputStream.flush();
//                    关闭
//                    outputStream.close();
//                    in.close();
//                    分段返回
//                    while ((len = bis.read(buf)) > 0) {
//                        outputStream.write(buf, 0, len);
//                        log.info("正在写入-------------{} ,{}",i++,buf);
//                    }
                }


            }
        }
        catch (IOException e) {
            System.out.println(e);
            response.setStatus(200);
            log.error("下载文件出错！", (Object) e.getStackTrace());
        }
        finally {
            if (ftpClient.isConnected()) {
                try {
                    ftpClient.disconnect();
                    log.info("成功销毁ftpClient客户端");
                } catch (IOException e) {
                    log.info("销毁失败upload{}", e);
                    e.printStackTrace();
                }
            }
            if (null != outputStream) {
                try {
                    outputStream.close();
                    log.info("成功关闭request.getOutPutStream获取的输出流");
                } catch (IOException e) {
                    log.error("关闭request.getOutPutStream获取的输出流出错！{}", (Object) e.getStackTrace());
                }
            }
            if (bis != null) {
                try {
                    in.close();
                    bis.close();
                    log.info("成功关闭缓冲输入流bis");
                } catch (IOException e) {
                    log.info("关闭缓冲输入流bis失败");
                    e.printStackTrace();
                }
            }
            if (in != null) {
                try {
                    in.close();
                    log.info("成功关闭ftp输入流in");
                    bis.close();
                    log.info("成功关闭ftp输入流in的buffer包装流bis");

//                    需要注意，在使用ftpClient.retrieveFileStream,一定要加completePendingCommand（）
//                    调用这个接口后，一定要手动close掉返回的InputStream，然后再调用completePendingCommand方法，若不是按照这个顺序，则会导致后面对FTPClient的操作都失败
//                    ftpClient.completePendingCommand();

                    log.info("成功关闭ftp输入流in的buffer包装流bis");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

//    public Map<String, Integer> analyzeRange2(String range, Integer fileSize) {
//        String[] split = range.split(-);
//        MapString, Integer result = new HashMap();
//        if (split.length == 1) {
//            从xxx长度读取到结尾
//            Integer startBytes = new Integer(range.replaceAll(bytes=, ).replaceAll(-, ));
//            result.put(startByte, startBytes);
//            result.put(endByte, fileSize - 1);
//        } else if (split.length == 2) {
//            从xxx长度读取到yyy长度
//            Integer startBytes = new Integer(split[0].replaceAll(bytes=, ).replaceAll(-, ));
//            Integer endBytes = new Integer(split[1].replaceAll(bytes=, ).replaceAll(-, ));
//            result.put(startByte, startBytes);
//            result.put(endByte, endBytes  fileSize  fileSize  endBytes);
//        } else {
//            log.info(未识别的range：, range);
//        }
//        return result;
//    }

//    @CrossOrigin
//    @PostMapping(value = "ftpfilesize")
//    public long getFileSize(String path, String fileName, String buildRange
//            , @RequestHeader String range) {
//        path = optftp_data;
//        fileName = cinatest.txt;
//        path = optwacossmpnasexclusive;
//        fileName = test8.ts;
//        FTPClient ftpClient = this.initFtpClient();
//        String LOCAL_CHARSET = UTF-8;
//        String allPath =  path + fileName ;
//        InputStream in = null;
//        BufferedInputStream bis = null;
//        try {
//            int index = path.indexOf();
//            int index1 = path.indexOf(, index + 2);
//            文件路径 去除掉ftp地址
//            String filePath = path.substring(index1);
//            String filePath = path;
//            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
//            String ext = fileName.substring(fileName.lastIndexOf(.)).toLowerCase();
//
//            String aa= URLEncoder.encode(fileName, UTF-8);
//
//            此句代码适用Linux的FTP服务器
//            String newPath = new String(fileName.getBytes(GBK), StandardCharsets.ISO_8859_1);
//            ftpClient.enterLocalPassiveMode();   设置被动模式，开通一个端口来传输数据
//
//                    获取ftp文件数据
//            FTPFile[] ftpFiles = ftpClient.listFiles(new String(allPath.getBytes(LOCAL_CHARSET), StandardCharsets.ISO_8859_1));
//            for (FTPFile fileftpFiles){
//                boolean result = existFile(ftpClient,filePath);
//                if (result){
//                    return file.getSize();
//                }
//            }
//        } catch (IOException e) {
//            System.out.println(e);
//            log.error(下载文件出错！, (Object) e.getStackTrace());
//        } finally {
//            if (ftpClient.isConnected()) {
//                try {
//                    ftpClient.disconnect();
//                } catch (IOException e) {
//                    log.info(销毁失败upload{}, e);
//                    e.printStackTrace();
//                }
//            }
//            if (bis != null) {
//                try {
//                    bis.close();
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            }
//            if (in != null) {
//                try {
//                    in.close();
//                    需要注意，在使用ftpClient.retrieveFileStream,一定要加completePendingCommand（）
//                    调用这个接口后，一定要手动close掉返回的InputStream，然后再调用completePendingCommand方法，若不是按照这个顺序，则会导致后面对FTPClient的操作都失败
//                    ftpClient.completePendingCommand();
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            }
//        }
//        return 0;
//    }



//    解析range，解析出起始byte（startByte）和结束byte（endBytes）
//
//    @param range    请求发来的range
//    @param fileSize 目标文件的大小
//      @return

    /**
     * 解析range，解析出起始byte（startByte）和结束byte（endBytes）
     *
     * @param range    请求发来的range
     * @param fileSize 目标文件的大小
     * @return
     */
    private Map<String, Integer> analyzeRange(String range, Integer fileSize) {
        String[] split = range.split("-");
        Map<String, Integer> result = new HashMap<>();
        if (split.length == 1) {
            //从xxx长度读取到结尾
            Integer startBytes = new Integer(range.replaceAll("bytes=", "").replaceAll("-", ""));
            result.put("startByte", startBytes);
            result.put("endByte", fileSize - 1);
        } else if (split.length == 2) {
            //从xxx长度读取到yyy长度
            Integer startBytes = new Integer(split[0].replaceAll("bytes=", "").replaceAll("-", ""));
            Integer endBytes = new Integer(split[1].replaceAll("bytes=", "").replaceAll("-", ""));
            result.put("startByte", startBytes);
            result.put("endByte", endBytes > fileSize ? fileSize : endBytes);
        } else {
            log.info("未识别的range：", range);
        }
        return result;
    }


//    获得客户端
    public FTPClient initFtpClient()  {
        FTPClient ftpClient = new FTPClient();
        try {
//            System.out.println(inputStream  +file);
            ftpClient.connect("192.168.89.130",21);
            ftpClient.login("cina","cs110120");
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            ftpClient.setControlEncoding("utf-8");
            int replyCode = ftpClient.getReplyCode(); //是否成功登录服务器
            if (!FTPReply.isPositiveCompletion(replyCode)) {
                log.info("connect failed...ftp服务器" + "192.168.163.130" + ":" + 21);
            }
            log.info("connect successful...ftp服务器" + "192.168.163.130" + ":" + 21);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return ftpClient;
    }

    public boolean existFile(FTPClient client,String filePath) throws IOException {
        boolean connected = client.isConnected();
        if (!connected) {
            System.out.println("客户端未连接成功！");
        }
//        进入当前用户根目录，方便listNames方法从用户根路径查找文件
//        client.changeWorkingDirectory();
//        文件再当前用户下的全路径
//        String path = demopdfdemo.jpeg;

//        当前文件存在时，会返回文件路径
        String[] listNames = client.listNames(filePath);
        boolean existsFlag = listNames.length > 0;
        return existsFlag;

    }


//    public static File multipartFileToFile(MultipartFile file) throws Exception {
//
//        File toFile = null;
//        if (file.equals()  file.getSize() = 0) {
//            file = null;
//        } else {
//            InputStream ins = null;
//            ins = file.getInputStream();
//            toFile = new File(file.getOriginalFilename());
//            inputStreamToFile(ins, toFile);
//            ins.close();
//        }
//        return toFile;
//    }
//
//    获取流文件
//    private static void inputStreamToFile(InputStream ins, File file) {
//        try {
//            OutputStream os = new FileOutputStream(file);
//            int bytesRead = 0;
//            byte[] buffer = new byte[8192];
//            while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
//                os.write(buffer, 0, bytesRead);
//            }
//            os.close();
//            ins.close();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }

}
